package exebuilder.core;

import exebuilder.FileUtil;
import exebuilder.command.CommandUtils;
import exebuilder.controller.EndController;
import exebuilder.controller.MainController;
import exebuilder.data.AppConfig;
import exebuilder.helper.ZipHelper;
import exebuilder.ui.MessageBox;
import javafx.application.Platform;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;

public class AppBuilder {

 private static final Logger logger = LogManager.getLogger();

 String moduleDeps;

 String runtimePath;

 AppConfig appConfig;

 String finalOutputPath;

 MainController mainController;
 EndController endController;

 public AppBuilder() {
  initData();
 }

 private void initData() {
  appConfig = AppConfig.getInstance();
  if (StringUtils.isBlank(appConfig.getAppName())) {
   appConfig.setAppName("MyApplication");
  }
  this.finalOutputPath = appConfig.getOutputPath() + "\\" + appConfig.getAppName();
  this.mainController = (MainController) Controllers.get("main");
  this.endController = (EndController) Controllers.get("end");
 }

 public void build() {
  try {
   mainController.backButton.setDisable(true);
   mainController.nextButton.setDisable(true);
   endController.buildInfoArea.clear();
   logger.debug("开始检测运行环境");
   initJavaEnv();
   logger.debug("开始创建输出目录");
   createOutputDir();
   logger.debug("开始分析运行依赖");
   analyzeDependencies();
   logger.debug("开始创建运行时");
   createRuntime();
   logger.debug("开始应用程序镜像");
   createAppImage();

  } catch (Exception e) {
   logger.error("command error:", e);
   MessageBox.error(e.getMessage());
  } finally {
   mainController.backButton.setDisable(false);
   mainController.nextButton.setDisable(false);
  }

 }

 private void initJavaEnv() throws Exception {
  ArrayList<String> commandLine = new ArrayList<>();
  commandLine.add("-version");
  String version = CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "java", commandLine).trim();
  logger.debug("java version:" + version);
 }

 private void createOutputDir() throws IOException {
  var outputDir = Paths.get(finalOutputPath);
  if (Files.exists(outputDir)) {
   FileUtil.deleteDir(finalOutputPath);
  }
  try {
   Files.createDirectory(outputDir);
   logger.debug("输出目录创建完毕");
   Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("输出目录：%s 创建成功\n", finalOutputPath)));
  } catch (IOException e) {
   logger.error("创建目录失败：", e);
   Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("创建目录失败：%s\n", e.getMessage())));
   throw e;
  }

 }

 private void analyzeDependencies() throws Exception {
  var mainJarPath = appConfig.getSourcePath() + "\\" + appConfig.getMainJarFile();
  var libPath = appConfig.getLibPath();
  ArrayList<String> commandLine = new ArrayList<>();
  commandLine.add("--multi-release");
  commandLine.add(appConfig.getJdkVersion().toString());
  commandLine.add("-q");
  commandLine.add("--print-module-deps");
  commandLine.add("--ignore-missing-deps");
  commandLine.add(mainJarPath);
  if (!StringUtils.isBlank(libPath)) {
   commandLine.add(libPath + "\\*.jar");
  }
  if (appConfig.getAppType() == 2) {
   Platform.runLater(() -> endController.buildInfoArea.appendText("解析SpringBoot模块...\n"));
   analyzeSpringBootJar();
   commandLine.add(finalOutputPath + "\\springboot_temp\\BOOT-INF\\lib\\*.jar");
  }
  moduleDeps = CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "jdeps", commandLine).trim();
  Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("查找依赖模块：%s\n", moduleDeps)));
  logger.debug("moduleDeps：{}", moduleDeps);
 }

 private void createRuntime() throws Exception {
  var runtimePath = finalOutputPath + "\\runtime";
  var runtimeDir = Paths.get(runtimePath);
  if (Files.exists(runtimeDir)) {
   FileUtil.deleteDir(runtimePath);
  }
  ArrayList<String> commandLine = new ArrayList<>();
  commandLine.add("--add-modules");
  commandLine.add(this.moduleDeps);
  commandLine.add("--no-header-files");
  commandLine.add("--no-man-pages");
  commandLine.add("--compress");
  commandLine.add("2");
  commandLine.add("--output");
  commandLine.add(runtimePath);
  String out = CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "jlink", commandLine);
  if (StringUtils.isBlank(out)) {
   logger.info("运行时镜像创建成功");
   Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("运行时镜像：%s 创建成功\n", runtimePath)));
   this.runtimePath = runtimePath;
  } else {
   logger.error("运行时镜像创建失败：{}", out);
   Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("运行时镜像创建失败： %s\n", out)));
  }
 }

 private void createAppImage() throws Exception {
  String imageOutputPath = finalOutputPath + "\\" + appConfig.getAppName() + "_unpack";
  if (Files.exists(Paths.get(imageOutputPath))) {
   FileUtil.deleteDir(imageOutputPath);
  }
  ArrayList<String> commandLine = new ArrayList<>();
  commandLine.add("--type");
  commandLine.add("app-image");
  commandLine.add("-i");
  commandLine.add(appConfig.getSourcePath());
  commandLine.add("-n");
  commandLine.add(appConfig.getAppName());
  commandLine.add("--main-jar");
  commandLine.add(".\\" + appConfig.getMainJarFile());
  commandLine.add("--main-class");
  commandLine.add(appConfig.getMainClass());
  commandLine.add("--runtime-image");
  commandLine.add(runtimePath);

  if (!StringUtils.isBlank(appConfig.getVersion())) {
   commandLine.add("--app-version");
   commandLine.add(appConfig.getVersion());
  }

  if (!StringUtils.isBlank(appConfig.getIconPath())) {
   commandLine.add("--icon");
   commandLine.add(appConfig.getIconPath());
  }
  if (!StringUtils.isBlank(appConfig.getCopyright())) {
   commandLine.add("--copyright");
   commandLine.add(appConfig.getCopyright());
  }
  commandLine.add("--dest");
  commandLine.add(imageOutputPath);
  if (appConfig.getAppType() == 1 || appConfig.getAppType() == 2) {
   commandLine.add("--win-console");
  }
  CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "jpackage", commandLine);
  deleteRuntimeDir();
  logger.info("执行完毕");
  Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("可执行文件：%s\\%s.exe 创建成功\n", imageOutputPath, appConfig.getAppName())));
  Platform.runLater(() -> endController.buildInfoArea.appendText("执行完毕\n"));
 }

 private void analyzeSpringBootJar() {
  Path path = Paths.get(finalOutputPath, "springboot_temp");
  try {
   if (!Files.exists(path)) {
    Files.createDirectory(path);
    logger.debug("start unzip springboot jar file");
    ZipHelper.unzip(appConfig.getSourcePath() + "\\" + appConfig.getMainJarFile(), path.toString());
    logger.debug("unzip sucess");
   }
  } catch (IOException e) {
   e.printStackTrace();
  }

 }

 private void deleteRuntimeDir() throws IOException {
  String runtimePath = finalOutputPath + "\\runtime";
  Path runtimeDir = Paths.get(runtimePath);
  if (Files.exists(runtimeDir)) {
   FileUtil.deleteDir(runtimePath);
  }
 }
}
